package cn.com.klun.tjy.web.annotations;

import cn.com.klun.tjy.web.annotations.base.ExtController;
import cn.com.klun.tjy.web.annotations.base.ExtRequestMapping;
import cn.com.klun.tjy.web.annotations.utils.ClassUtil;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/***
 * 手写SpringMvc框架 
 * 思路： 
 * 1.手动创建一个DispatcherServlet 拦截项目的所有请求 SpringMvc 基于servlet实现 
 * 2.extends HttpHttpServlet 重写init 方法 
 *  2.1 判断扫包路径下面是否有ExtController注解，并且注入到SpringMvc容器中，存放在springmvcBeans集合当中
 * key为 默认类名小写，value 为对象 
 *  2.2 将url映射和方法进行关联  
 *  2.3判断类上是否有ExtRequestMaping注解，使用java反射机制循环遍历的方法，再判断方法上是否有注解，将url和方法对应的存入urlBeans集合中 
 * 3.处理请求，重写get，post方法  
 *  3.1获取到请求的url，与初始化得到的方法进行匹配，获取到实例的对象，urlMethodsBeans调用对象获取方法名称，使用反射机制进行执行。
 *
 * @author DateLoader
 *
 */
public class ExtDispatcherServlet extends HttpServlet {
    
    // springmvc 容器对象 key:类名id ,value 对象
    private ConcurrentHashMap<String, Object> springmvcBeans = new ConcurrentHashMap<String, Object>();
    // springmvc 容器对象 key:请求地址 ,value类
    private ConcurrentHashMap<String, Object> urlBeans = new ConcurrentHashMap<String, Object>();
    // springmvc 容器对象 key:请求地址 ,value 方法
    private ConcurrentHashMap<String, String> urlMethodsBeans = new ConcurrentHashMap<String, String>();

    // DispatcherServlet 初始化操作
    @Override
    public void init() throws ServletException {
        // 1.定义一个扫包的路径，进行扫包获取全部的类
        List<Class<?>> listClass = ClassUtil.getClasses("cn.com.klun.tjy.service.impl");
        // 2.将获取到的所有类全部注入到Springmvc容器中，并且存放在springmvcBeans集合中
        try {
            findClassMvcAnnotation(listClass);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 3将url映射和方法进行关联
        handlerMapping();
    }

    // 4.处理请求，重写get，post方法
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1.获取请求的URL
        String requestURI = req.getRequestURI();
        if (StringUtils.isEmpty(requestURI)) {
            return;
        }
        // 2.从urlBeans获取对象
        Object object = urlBeans.get(requestURI);
        if (object == null) {
            resp.getWriter().print("not found 404 url");
            return;
        }
        // 3.使用url地址获取方法
        String urlName = urlMethodsBeans.get(requestURI);
        if (StringUtils.isEmpty(urlName)) {
            resp.getWriter().print("not found method");
        }
        // 4.使用反射机制执行方法
        String methodInvoke = (String) methodInvoke(object, urlName);
        //返回数据
        resp.getWriter().print(methodInvoke);
        // 5.如果想调用视图转换器渲染给页面展示
        //extResourceViewResolver(methodInvoke, req, resp);
    }

    // 4.使用反射机制执行方法
    public Object methodInvoke(Object object, String urlName) {
        Class<? extends Object> classInfo = object.getClass();
        try {
            Method method = classInfo.getMethod(urlName);
            Object result = method.invoke(object);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private void extResourceViewResolver(String pageName, HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException {
        // 根路径
        String prefix = "/";
        String suffix = ".jsp";
        req.getRequestDispatcher(prefix + pageName + suffix).forward(req, res);
    }

    // 2.将获取到的所有类全部注入到Springmvc容器中，并且存放在springmvcBeans集合中
    public void findClassMvcAnnotation(List<Class<?>> listClass) throws InstantiationException, IllegalAccessException {
        for (Class<?> classInfo : listClass) {
            // 判断类上是否有@ExtController注解
            ExtController extControllerAnnotation = classInfo.getDeclaredAnnotation(ExtController.class);
            if (extControllerAnnotation != null) {
                // 根据ClassUtil 将类名称首字母转换为小写
                String simpleName = ClassUtil.toLowerCaseFirstOne(classInfo.getSimpleName());
                // 实例化对象，并且存入到 springmvcBeans
                Object newInstance = classInfo.newInstance();
                springmvcBeans.put(simpleName, newInstance);
            }
        }
    }

    // 将url映射和方法进行关联
    public void handlerMapping() {
        for (Map.Entry<String, Object> springEntry : springmvcBeans.entrySet()) {
            // 遍历初始化注入到SpringMvc容器中的类，是否含有url映射注解
            Object object = springEntry.getValue();
            // 判断类上是否有 @ExtRequestMaping
            Class<? extends Object> classInfo = object.getClass();
            ExtRequestMapping extRequestMappingAnnotation = classInfo.getDeclaredAnnotation(ExtRequestMapping.class);
            String baseUrl = "";
            if (extRequestMappingAnnotation != null) {
                // 获取类上的注解value 值
                baseUrl = extRequestMappingAnnotation.value();
            }
            // 获取类的所有方法，判断是否有@ExtRequestMaping
            Method[] declaredMethods = classInfo.getDeclaredMethods();
            // 循环得到的所有方法，进行判断
            for (Method method : declaredMethods) {
                ExtRequestMapping declaredAnnotation = method.getDeclaredAnnotation(ExtRequestMapping.class);
                if (declaredAnnotation != null) {
                    // 如果类上有注解那么需要和类上的注解进行拼接
                    String methodurl = baseUrl + declaredAnnotation.value();
                    // 将获得的url 存入urlBeans
                    urlBeans.put(methodurl, object);
                    urlMethodsBeans.put(methodurl, method.getName());
                }
            }
        }
    }
}